#include "Level.h"
#include "../Graphic/GraphicManager.h"
#include "../Game/Game.h"
#include "../Rules/LevelRule.h"
#include "../Rules/Statement.h"

//Load the map from a XML element
void cLevel::LoadMap(TiXmlElement *lpMap)
{
 unsigned int luiRowNumber = 0;
 //Process each row
 for(TiXmlElement *lpRow = lpMap->FirstChildElement(); lpRow != NULL; lpRow = lpRow->NextSiblingElement()) 
  {
    //Get the row
    std::string lsRow = lpRow->GetText();
    for (unsigned int luiIndex = 0; luiIndex < lsRow.size(); luiIndex++)
    {
      //If its a wall puts false into mWalss 
      unsigned int luiPosition = (luiRowNumber * miLevelWidth) + luiIndex;
      mWalls[luiPosition] = ( lsRow[luiIndex] != '#');
   
      //Add special cells to a dictionary
      if (lsRow[luiIndex] != ' ' && lsRow[luiIndex] != '#')
      {
        cSpecialCell lcCell;
        lcCell.mcId = lsRow[luiIndex];
        lcCell.miX = luiIndex;
        lcCell.miY = luiRowNumber;
        mDictionary.push_back(lcCell);
      }
    }

    //Update row number
    luiRowNumber++;
  }
}

//Load the cells from a XML element
void cLevel::LoadCells(TiXmlElement *lpCells)
{
 std::ostringstream lsMessage; 

 //Process each cell
 for(TiXmlElement *lpCell = lpCells->FirstChildElement(); lpCell != NULL; lpCell = lpCell->NextSiblingElement()) 
  {
    char lcCellid = ' ';
    std::string lsType = "";
    std::string lsName = "";
    char lcSymbol = ' ';
    eSpecialCellContent leType = eObject;
    bool lbFound = false;
    

    //Get the attributes
    lpCell->QueryValueAttribute("cellId",&lcCellid);
    lpCell->QueryValueAttribute("symbol",&lcSymbol);

    //Due a bug related with white spaces in QueryValueAttribute we have to use
    //Attribute to get strings 
    if (lpCell->Attribute("contentType"))
      lsType = lpCell->Attribute("contentType");

    if (lpCell->Attribute("name"))
      lsName = lpCell->Attribute("name");

    if (lsType == "Player")
      leType = ePlayer;

    if (lsType == "Enemy")
      leType = eEnemy;

    if (lsType == "Object")
      leType = eObject;

    if (lsType == "Special Location")
      leType = eSpecialLocation;

    //Seek for the element in the dictionary
    for (cSpecialCellDictionaryIt lcIt = mDictionary.begin(); lcIt != mDictionary.end(); lcIt++)
    {
      //If the id id found establish the values
      if ((*lcIt).mcId == lcCellid)
      {
        (*lcIt).meCellContent = leType;
        (*lcIt).mName = lsName;
        (*lcIt).mcSymbol = lcSymbol;

        if ((leType != eSpecialLocation))
        {
           cObject *lcObject;
           switch (leType)
           {
             //Create an instance of object
             case eObject:
               lcObject = new cObject();
               break;
             //Create an instance of player
             case ePlayer:
               lcObject = new cPlayer();
               moPlayer = lcObject;
               break;
             //Create an instance of enemy
             case eEnemy:
               lcObject = new cEnemy();
               break;
             default:
               break;
           }
           
           lcObject->SetType(leType);
           lcObject->SetName(lsName);
           lcObject->SetSymbol(lcSymbol);
           lcObject->SetX((*lcIt).miX);
           lcObject->SetY((*lcIt).miY);

           //If its an enemy add it to enemy group
           if (leType != eEnemy)
             mObjects.push_back(lcObject);
           else
             mEnemies.AddEnemy((cEnemy *)lcObject);
        }

        lbFound = true;
    //    break;
      }
    }


    //All the special cells must exists in the map
    assert(lbFound==true);
  }
}

//Load the rules from a XML element
void cLevel::LoadRules(TiXmlElement *lpRules)
{
 std::ostringstream lsMessage; 
 

 //Process each rule
 for(TiXmlElement *lpRule = lpRules->FirstChildElement(); lpRule != NULL; lpRule = lpRule->NextSiblingElement()) 
  {
    cLevelRule lcRule;
 
    //Get every condition
    for (TiXmlElement *lpCondition = lpRule->FirstChildElement("Conds")->FirstChildElement(); lpCondition != NULL; lpCondition = lpCondition->NextSiblingElement())
    { 
      cStatement lcCondition;
      
      //Content statement
      if (lpCondition->ValueStr() == "Content")
      {
        //Set the type
        lcCondition.SetType(eStatementContent);

        //Set parameters
        if (lpCondition->Attribute("cellId"))
          lcCondition.SetParam1(lpCondition->Attribute("cellId"));
      
        if (lpCondition->Attribute("name"))
          lcCondition.SetParam2(lpCondition->Attribute("name"));
      }

      //Capture statement
      if (lpCondition->ValueStr() == "Capture")
      {
        //Set the type
        lcCondition.SetType(eStatementCapture);

        //Set the parameter
        if (lpCondition->Attribute("name"))
          lcCondition.SetParam1(lpCondition->Attribute("name"));
      }

      //Add condition
      lcRule.AddConditionStatement(lcCondition);
    }

    //Get every action
    for (TiXmlElement *lpAction = lpRule->FirstChildElement("Then")->FirstChildElement(); lpAction != NULL; lpAction = lpAction->NextSiblingElement())
    {      
      cStatement lcAction;
      
      //Remove rules
      if (lpAction->ValueStr()=="Remove")
      {
        lcAction.SetType(eStatementRemove);

        if (lpAction->Attribute("name"))
          lcAction.SetParam1(lpAction->Attribute("name"));
      }

      //Message rules
      if (lpAction->ValueStr()=="Message")
      {
        lcAction.SetType(eStatementMessage);

        if (lpAction->Attribute("text"))
          lcAction.SetParam1(lpAction->Attribute("text"));
      }

      //Move rules
      if (lpAction->ValueStr()=="Move")
      {
        lcAction.SetType(eStatementMove);

        if (lpAction->Attribute("name"))
          lcAction.SetParam1(lpAction->Attribute("name"));
        
        if (lpAction->Attribute("cellId"))
          lcAction.SetParam2(lpAction->Attribute("cellId"));
      }

      //Exit rules
      if (lpAction->ValueStr()=="Exit")
      {
        lcAction.SetType(eStatementExit);
      }

      lcRule.AddActionStatement(lcAction);
    }

    //Add every rule to the rules
    mRules.AddRule(lcRule);
  }
}

//Load the level from an XML file
void cLevel::Load( std::string lsFilename )
{
  TiXmlDocument lxDoc(lsFilename);

  //Load document
  bool lbLoadOk = lxDoc.LoadFile();

  //If fail goes out
  if (!lbLoadOk )
  {
    OutputDebugString("XML Load: FAILED\n");
    system("pause");
    exit(-1);
  }

  OutputDebugString("XML Load: OK\n");
  

 	TiXmlHandle lhDoc(&lxDoc);
	TiXmlElement *lpElem;
	TiXmlHandle lhRoot(0);

  //Seek for root element (level)
	lpElem=lhDoc.FirstChildElement().Element();
	if (!lpElem)
  {
    OutputDebugString("XML Load: FAILED. No root element\n");
    system("pause");
    exit(-2);
  }

  //Assign the width and height
  lpElem->QueryIntAttribute("width", &miLevelWidth);
  lpElem->QueryIntAttribute("height", &miLevelHeight);
  //Create the walls
  mWalls = new bool[miLevelWidth * miLevelHeight];

  std::ostringstream lsMessage; 
  //Read all the childs of level
  for(TiXmlElement *lpChild = lpElem->FirstChildElement(); lpChild != NULL; lpChild = lpChild->NextSiblingElement()) 
  {
 
    //If its a map Load it
    if (lpChild->ValueStr() == "Map")
      LoadMap(lpChild);

    //Load Special cells
    if (lpChild->ValueStr() == "SpecialCells")
      LoadCells(lpChild);

    //Load Rules
    if (lpChild->ValueStr() == "Rules")
      LoadRules(lpChild);
  }
}


//Return true if the position is a wall
bool cLevel::IsWall(unsigned int luiX, unsigned int luiY)
{
  unsigned int luiPosition = (luiY * miLevelWidth) + luiX;
  return !mWalls[luiPosition];
}


//Return true if the object ObjectName is in the cell CellId
bool cLevel::IsInCell(char lcCellId, std::string lsObjectName)
{

  unsigned int luiCellX;
  unsigned int luiCellY;
  bool lbFound = false;

  //Seek for the cellId
  for (cSpecialCellDictionaryIt lcIt = mDictionary.begin(); lcIt != mDictionary.end(); lcIt++)
  {
    //If the id id found get the values
    if ((*lcIt).mcId == lcCellId)
    {
      luiCellX = (*lcIt).miX;
      luiCellY = (*lcIt).miY;
      lbFound = true;
    }
  }

  //CellId not found!
  if (lbFound == false) return false;

  //Seek for object and compare position
  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    if ((*lcIt)->GetName() == lsObjectName)
    {
      //Return true if there are in the same cell, false otherwise
      return ((luiCellX == (*lcIt)->GetX()) && (luiCellY == (*lcIt)->GetY()));
    }
  }

  //Object not found!
  return false;
}

//Return true if the object lsObjectName is in the player cell 
bool cLevel::IsInPlayerCell(std::string lsObjectName)
{
  //Seek for object and compare position
  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    if ((*lcIt)->GetName() == lsObjectName)
    {
      //Return true if there are in the same cell, false otherwise
      return (moPlayer->GetOrientation((*lcIt)->GetX(), (*lcIt)->GetY()) == eO_Over);
    }
  }

  //Object not found!
  return false;
}
//Remove object from the list of objects and delete it
void cLevel::RemoveObject(std::string lsObjectName)
{
  //Seek for object and compare position
  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    if ((*lcIt)->GetName() == lsObjectName)
    {
      delete (*lcIt);
      mObjects.erase(lcIt);
      return;
    }
  }
}

//Show a message at row 24
void cLevel::ShowMessage()
{
  cGraphicManager &lGraphic = cGraphicManager::Get();
  lGraphic.SetColor(eLightWhite,eBlack);
  lGraphic.WriteChars(0, 24, (char *)mMessage.c_str());
}

//Move object lsObjectName to lcCellId position
void cLevel::MoveObject(std::string lsObjectName, char lcCellId)
{
  unsigned int luiCellX;
  unsigned int luiCellY;
  bool lbFound = false;

  //Seek for the cellId
  for (cSpecialCellDictionaryIt lcIt = mDictionary.begin(); lcIt != mDictionary.end(); lcIt++)
  {
    //If the id id found get the values
    if ((*lcIt).mcId == lcCellId)
    {
      luiCellX = (*lcIt).miX;
      luiCellY = (*lcIt).miY;
      lbFound = true;
    }
  }

  //CellId not found!
  if (lbFound == false) return;

  //Seek for object and moveit
  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    if ((*lcIt)->GetName() == lsObjectName)
    {
      (*lcIt)->SetX(luiCellX);
      (*lcIt)->SetY(luiCellY);
      return;
    }
  }
}

//Quit level
void cLevel::QuitLevel()
{
  //Quit the game
  cGame::Get().Finish();
}

//Init the level
void cLevel::Init(const std::string lsFilename)
{
  //Clear time counter
  mfMessageElapsedTime = 0.0f;
  //Load level
  Load(lsFilename);
}

//Update the level
void cLevel::Update(float lfTimeStep)
{
  //If there a message wait 200 ms before clear it
  if (mMessage != "")
  {
    //Update time counter
    mfMessageElapsedTime += lfTimeStep;
    //Do some actions after 2.00 s
    if (mfMessageElapsedTime > 2.000)
    {
      mMessage = "";
      mfMessageElapsedTime = 0;
    }
  }

  //Update every object
  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    (*lcIt)->Update(lfTimeStep);
  }

  //Update enemies
  mEnemies.Update(lfTimeStep);

  //Evaluate rules
  mRules.EvalRules();
}

//Render the level
void cLevel::Render()
{
  //Get the graphic manager
  cGraphicManager &lGraphic = cGraphicManager::Get();

  //Render the special cells
  lGraphic.SetColor(eGreen, eBlack);
  for (cSpecialCellDictionaryIt lcIt = mDictionary.begin(); lcIt != mDictionary.end(); lcIt++)
  {
    cSpecialCell lcActualCell = (*lcIt);
    if (lcActualCell.meCellContent == eSpecialLocation)
      lGraphic.WriteChar(lcActualCell.miX, lcActualCell.miY, lcActualCell.mcSymbol);
  }
 
  //Render the objects
  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    (*lcIt)->Render();
  }

  //Render the enemies
  mEnemies.Render();

  //Render the level
  for(int liX = 0; liX < miLevelWidth; liX++)
    for (int liY = 0; liY < miLevelHeight; liY++)
    {
      if (IsWall(liX, liY)) 
      {
        lGraphic.SetColor( eLightWhite, eRed);  
        lGraphic.WriteChar(liX, liY, '#');
      }

    }

  //Render Message
  ShowMessage();
}

//Deinit the level
void cLevel::Deinit()
{
  //Remove walls
  if (mWalls != NULL)
  {
    delete mWalls;
    mWalls = NULL;
  }

  //Remove player pointer
  moPlayer = NULL;

  //Remove objects
  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    delete (*lcIt);
  }
  mObjects.clear();

  //Rules are destroyed in destructor of the classes

}

